
#define ATRACE_TAG ATRACE_TAG_GRAPHICS

#include <iostream>

#include <utils/Log.h>
#include <utils/Trace.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/Surface.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <GLES2/gl2.h>

#include "gpu/GrDirectContext.h"
#include "gpu/gl/GrGLInterface.h"
#include "core/SkData.h"
#include "core/SkImage.h"
#include "core/SkStream.h"
#include "core/SkCanvas.h"
#include "core/SkSurface.h"
#include "core/SkBitmap.h"
#include "core/SkImageEncoder.h"
#include "core/SkTileMode.h"
#include "core/SkFont.h"
#include "core/SkShader.h"
#include "encode/SkPngEncoder.h"


using namespace android;

using Transaction = SurfaceComposerClient::Transaction;

void initGpuEnv();
void drawWithGpu();

static sk_sp<SkShader> make_bw_dither() {
    auto surf = SkSurface::MakeRasterN32Premul(2, 2);
    surf->getCanvas()->drawColor(SK_ColorWHITE);
    surf->getCanvas()->drawRect({0, 0, 1, 1}, SkPaint());
    surf->getCanvas()->drawRect({1, 1, 2, 2}, SkPaint());
    return surf->makeImageSnapshot()->makeShader(SkTileMode::kRepeat,
                                                 SkTileMode::kRepeat,
                                                 SkSamplingOptions(SkFilterMode::kLinear));
}

void draw(SkCanvas* canvas) {
    canvas->drawColor(SK_ColorWHITE);
    SkFont font(nullptr, 12);

    // BW Dither
    canvas->translate(5, 5);
    SkPaint p;
    p.setShader(make_bw_dither());
    canvas->drawRect({0, 0, 100, 100}, p);
    SkPaint black;
    canvas->drawString("BW Dither", 0, 125, font, black);

    // Scaled BW Dither
    canvas->translate(105, 0);
    canvas->save();
    canvas->scale(0.5, 0.5);
    canvas->drawRect({0, 0, 200, 200}, p);
    canvas->restore();
    canvas->drawString("Scaled Dither", 0, 125, font, black);

    // Blend black on to white
    canvas->translate(105, 0);
    p.setColor(0x80000000);
    p.setShader(nullptr);
    canvas->drawRect({0, 0, 100, 100}, p);
    p.setShader(nullptr);
    canvas->drawString("Blend", 0, 125, font, black);

    // Opaque color (0xFFBCBCBC)
    canvas->translate(105, 0);
    p.setColor(0xFFBCBCBC);
    canvas->drawRect({0, 0, 100, 100}, p);
    canvas->drawString("0xFFBCBCBC", 0, 125, font, black);

    // Opaque color (0xFF808080)
    canvas->translate(105, 0);
    p.setColor(0xFF808080);
    canvas->drawRect({0, 0, 100, 100}, p);
    canvas->drawString("0xFF808080", 0, 125, font, black);
}

int main(int argc, char** argv) {
    (void)argc;
    (void)argv;
    ATRACE_NAME("HELLO_WORLD");

    initGpuEnv();

    ALOGE("++++++++++++++++++++++++++++++++++++++++++++");
    //drawWithGpu();

    int width = 300;
    int height = 400;
    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(width, height);
    SkCanvas* canvas = surface->getCanvas();
    draw(canvas);
    sk_sp<SkImage> img(surface->makeImageSnapshot());
    if (!img) {
        ALOGE("Failed to makeImageSnapshot");
        return 0;
    }

    std::vector<int32_t> pixels;
    pixels.resize(img->height() * img->width() * 4);
    SkPixmap pixmap(SkImageInfo::Make(img->width(), img->height(), kN32_SkColorType,
            img->alphaType()), (const void*) &pixels.front(), img->width() * 4);
    if (!img->readPixels(pixmap, 0, 0)) {
        ALOGE("Failed to read pixel from SkImage to SkPixmap.");
    } else {
        ALOGD("Successfully read pixel from SkImage to SkPixmap.");
    }

    SkString path;
    path.printf("/sdcard/Download/RasterPicture.png");
    SkFILEWStream out(path.c_str());

    SkPngEncoder::Encode(&out, pixmap, {});
    out.flush();

    ALOGE("--------------------------------------------");
    return 0;
}

void initGpuEnv() {
    // Create window Surface
    ATRACE_NAME("CreateWindowSurface");
    uint32_t width = 400;
    uint32_t height = 300;
    sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
    sp<SurfaceControl> surfaceControl = composerClient->createSurface(
                String8("Hello World Surface"), width, height,
                PIXEL_FORMAT_RGB_888, 0);
    Transaction t;
    t.setLayer(surfaceControl, 0x7FFFFFFF)
        .show(surfaceControl)
        .apply();
    sp<ANativeWindow> window = surfaceControl->getSurface();
    ATRACE_END();


    // init egl
    ATRACE_NAME("Init EGL/GL");
    EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    EGLint majorVersion;
    EGLint minorVersion;
    EGLConfig  glConfig;
    eglInitialize(eglDisplay, &majorVersion, &minorVersion);
    EGLint numConfigs = 0;
    const EGLint defaultConfigAttribs[] = {
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 16,
        EGL_STENCIL_SIZE, 8,
        EGL_NONE };
    eglChooseConfig(eglDisplay, defaultConfigAttribs, &glConfig, 1, &numConfigs);

    EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, glConfig, window.get(), nullptr);

    const EGLint defaultContextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE };

    EGLContext eglContext = eglCreateContext(eglDisplay, glConfig, EGL_NO_CONTEXT, defaultContextAttribs);
    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

    EGLint w, h;
    eglQuerySurface(eglDisplay, eglSurface, EGL_WIDTH, &w);
    eglQuerySurface(eglDisplay, eglSurface, EGL_HEIGHT, &h);
    ALOGD("EGLSurface: w=%d, h=%d", w, h);

    glViewport(0, 0, w, h);

    GLubyte* vendor = (GLubyte*)glGetString(GL_VENDOR);
    GLubyte* version = (GLubyte*)glGetString(GL_VERSION);
    GLubyte* renderer = (GLubyte*)glGetString(GL_RENDERER);
    GLubyte* glslVersion = (GLubyte*)glGetString(GL_SHADING_LANGUAGE_VERSION);
    ALOGD("OpenGL INFO:");
    ALOGD("|-Vendor: %s", vendor);
    ALOGD("|-Version: %s", version);
    ALOGD("|-GLSL Version: %s", glslVersion);
    ALOGD("|-Renderer: %s", renderer);
    ALOGD("\n");
    ATRACE_END();
}

void drawWithGpu() {
    ATRACE_NAME("Init skia gpu backend");
    sk_sp<const GrGLInterface> interface = nullptr;
    sk_sp<GrDirectContext> context = GrDirectContext::MakeGL(interface);
    SkImageInfo info = SkImageInfo:: MakeN32Premul(400, 300);
    sk_sp<SkSurface> gpuSurface = SkSurface::MakeRenderTarget(context.get(), skgpu::Budgeted::kNo, info);
    SkCanvas* canvas = gpuSurface->getCanvas();
    ATRACE_END();
    ATRACE_NAME("Draw");
    canvas->save();
    canvas->translate(SkIntToScalar(128), SkIntToScalar(128));
    canvas->rotate(SkIntToScalar(45));
    SkRect rect = SkRect::MakeXYWH(-90.5f, -90.5f, 181.0f, 181.0f);
    SkPaint paint;
    paint.setColor(SK_ColorBLUE);
    canvas->drawRect(rect, paint);
    canvas->restore();
    ATRACE_END();
    ATRACE_NAME("MakeImageSnapshot");
    sk_sp<SkImage> image(gpuSurface->makeImageSnapshot());
    ATRACE_END();

    // case 1: copy pixel from SkSurface to SkPixmap and save it to file
    ATRACE_NAME("SaveSkPixmap");
    ATRACE_NAME("ReadSkPixmap");
    std::vector<int32_t> pixels;
    pixels.resize(image->height() * image->width() * 4);
    SkPixmap skpixmap(SkImageInfo::Make(image->width(), image->height(), kN32_SkColorType,
            image->alphaType()), (const void*) &pixels.front(), image->width() * 4);
    if (!image->readPixels(skpixmap, 0, 0)) {
        ALOGE("Failed to read pixel from SkImage to SkPixmap.");
    } else {
        ALOGD("Successfully read pixel from SkImage to SkPixmap.");
    }
    ATRACE_END();

    ATRACE_NAME("WriteSkPixmap");
    SkString filename_pixmap;
    filename_pixmap.printf("/sdcard/Download/hello_pixmap.png");
    SkFILEWStream outStream_pixmap(filename_pixmap.c_str());
    if (SkPngEncoder::Encode(&outStream_pixmap, skpixmap, SkPngEncoder::Options())) {
        outStream_pixmap.flush();
        ALOGI("Successfully save %s file.", filename_pixmap.c_str());
    } else {
        ALOGE("Failed to save to file.");
    }
    ATRACE_END();
    ATRACE_END();

    // case 2: copy pixel from SkSurface to SkBitmap and save it to file
    ATRACE_NAME("SaveSkBitmap");
    ATRACE_NAME("ReadSkBitmap");

    SkImageInfo bitmapInfo = SkImageInfo::Make(300, 300, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
    SkBitmap* bitmap = new SkBitmap;
    if (!bitmap->tryAllocPixels(bitmapInfo)) {
        ALOGE("Failed to alloc pixel for bitmap.");
    }
    if (!gpuSurface->readPixels(*bitmap, 0, 0)) {
        ALOGE("Failed to read pixel from gpuSurface.");
    }
    ATRACE_END();

    ATRACE_NAME("WriteSkBitmap");
    SkString filename;
    filename.printf("/sdcard/Download/hello.png");
    SkFILEWStream outStream(filename.c_str());
    SkPixmap pixmap;
    bitmap->peekPixels(&pixmap);
    if (SkPngEncoder::Encode(&outStream, pixmap, SkPngEncoder::Options())) {
        outStream.flush();
        ALOGI("Successfully save %s file.", filename.c_str());
    } else {
        ALOGE("Failed to save to file.");
    }
    ATRACE_END();
    ATRACE_END();
}

